package com.zxy.paycenter.modules.alipay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AccessParams;
import com.alipay.api.domain.AgreementParams;
import com.alipay.api.domain.ExtendParams;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.xiaoleilu.hutool.util.NumberUtil;
import com.zxy.paycenter.common.config.DefaultAlipayClientFactory;
import com.zxy.paycenter.common.constants.Constant;
import com.zxy.paycenter.common.message.request.*;
import com.zxy.paycenter.common.utils.ZxingUtils;
import com.zxy.paycenter.modules.alipay.service.AlipayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Properties;

/**
 * @author zxy
 */
@Slf4j
@Service
public class AlipayServiceImpl implements AlipayService {
    @Override
    public String withholdPay(CommodityReq commodityReq, Properties properties) {
        String orderString = Constant.FAIL;
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.pay
        AlipayTradePayRequest alipayRequest = new AlipayTradePayRequest();
        String notifyUrl = getNotifyUrl(commodityReq, properties);
        String biz = getBizContent(commodityReq, properties, "withholdPay");
        alipayRequest.setBizContent(biz);
        log.info("notifyUrl:{}", notifyUrl);
        //后台回调
        alipayRequest.setNotifyUrl(notifyUrl);
        log.info("biz:{}", biz);
        try {
            // execute api调用 无页面输出
            AlipayTradePayResponse response = alipayClient.execute(alipayRequest);
            log.info("alipayResponse:{}", JSONObject.toJSONString(response));
            if (response.isSuccess()) {
                orderString = response.getBody();
            } else {
                log.info("支付宝支付错误 alipayResponse:{}", JSONObject.toJSONString(response));
            }
        } catch (AlipayApiException e) {
            log.error("商户代扣出错", e);
        }
        return orderString;
    }

    @Override
    public String aliPayPc(CommodityReq commodityReq, Properties properties) {
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        String returnUrl = getReturnUrl(commodityReq, properties);
        String notifyUrl = getNotifyUrl(commodityReq, properties);
        //前台通知
        alipayRequest.setReturnUrl(returnUrl);
        //后台回调
        alipayRequest.setNotifyUrl(notifyUrl);
        String biz = getBizContent(commodityReq, properties, "aliPayPc");
        alipayRequest.setBizContent(biz);
        log.info("业务参数:" + alipayRequest.getBizContent());
        String form = Constant.FAIL;
        try {
            // pageExecute 手机H5和PC 有页面输出
            form = alipayClient.pageExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            log.error("支付宝构造表单失败", e);
        }
        return form;
    }

    @Override
    public String aliPayMobile(CommodityReq commodityReq, Properties properties) {
        log.info("支付宝手机支付下单");
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
        String returnUrl = getReturnUrl(commodityReq, properties);
        String notifyUrl = getNotifyUrl(commodityReq, properties);
        //前台通知
        alipayRequest.setReturnUrl(returnUrl);
        //后台回调
        alipayRequest.setNotifyUrl(notifyUrl);
        String biz = getBizContent(commodityReq, properties, "mobilePay");
        alipayRequest.setBizContent(biz);
        log.info("业务参数:" + alipayRequest.getBizContent());
        String form = Constant.FAIL;
        try {
            // pageExecute 手机H5和PC 有页面输出
            form = alipayClient.pageExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            log.error("支付宝构造表单失败", e);
        }
        return form;
    }

    @Override
    public String aliQrCodePay(CommodityReq commodityReq, Properties properties) {
        log.info("订单号：{}生成支付宝支付码", commodityReq.getOutTradeNo());
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        String message = Constant.SUCCESS;
        //二维码存放路径
        log.info("二维码存放地址:{}", Constant.QRCODE_PATH);
        AlipayTradePrecreateRequest alipayRequest = new AlipayTradePrecreateRequest();
        String imgPath = Constant.QRCODE_PATH + Constant.SF_FILE_SEPARATOR + commodityReq.getOutTradeNo() + ".png";
        String notifyUrl = getNotifyUrl(commodityReq, properties);
        BigDecimal totalAmount = NumberUtil.div(commodityReq.getTotalFee(), "100", 2);
        // toPlainString方法 避免输出科学计数法
        commodityReq.setTotalFee(totalAmount.toPlainString());
        String biz = getBizContent(commodityReq, properties, "aliQrCodePay");
        alipayRequest.setBizContent(biz);
        alipayRequest.setNotifyUrl(notifyUrl);
        try {
            AlipayTradePrecreateResponse response = alipayClient.execute(alipayRequest);
            if (response.isSuccess()) {
                log.info("支付宝预下单成功");
                ZxingUtils.getQRCodeImge(response.getQrCode(), 256, imgPath);
            } else {
                log.info("支付宝预下单失败");
                message = Constant.FAIL;
            }
        } catch (AlipayApiException e) {
            log.error("支付宝构造表单失败", e);
        }
        return message;
    }

    @Override
    public String appServicePay(CommodityReq commodityReq, Properties properties) {
        String orderString = Constant.FAIL;
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest alipayRequest = new AlipayTradeAppPayRequest();
        String notifyUrl = getNotifyUrl(commodityReq, properties);
        alipayRequest.setNotifyUrl(notifyUrl);
        String biz = getBizContent(commodityReq, properties, "appServicePay");
        alipayRequest.setBizContent(biz);
        try {
            // 这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(alipayRequest);
            orderString = response.getBody();
        } catch (AlipayApiException e) {
            log.error("app支付服务端支付失败", e);
        }
        return orderString;
    }

    @Override
    public String userAgreementSign(MemberReq memberReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayUserAgreementPageSignRequest alipayRequest = new AlipayUserAgreementPageSignRequest();
        String notifyUrl = properties.getProperty("notifyUrl");
        if (StringUtils.isNotBlank(memberReq.getExceptionNotifyUrl())) {
            notifyUrl = memberReq.getExceptionNotifyUrl();
            log.info("商户调用第三方回调地址：{}", notifyUrl);
        }
        String returnUrl = memberReq.getReturnUrl();
        alipayRequest.setNotifyUrl(notifyUrl);
        alipayRequest.setReturnUrl(returnUrl);
        properties.setProperty("returnUrl", returnUrl);
        JSONObject bizContent = new JSONObject();
        bizContent.put("external_logon_id", memberReq.getMemberId());
        bizContent.put("personal_product_code", "GENERAL_WITHHOLDING_P");
        AccessParams accessParams = new AccessParams();
        accessParams.setChannel("ALIPAYAPP");
        bizContent.put("access_params", JSONObject.toJSONString(accessParams).replaceAll("\"", "'"));
        String biz = bizContent.toString().replaceAll("\"", "'");
        alipayRequest.setBizContent(biz);
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        try {
            AlipayUserAgreementPageSignResponse alipayResponse = alipayClient.sdkExecute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                log.info("{}用户支付宝签约成功", memberReq.getMemberId());
                orderString = properties.getProperty("agreementUrl") + URLEncoder.encode(alipayResponse.getBody(), "utf-8");
            } else {
                log.info("{}用户支付宝签约失败", memberReq.getMemberId());
            }
        } catch (AlipayApiException e) {
            log.error("用户支付宝签约失败", e);
        } catch (UnsupportedEncodingException e) {
            log.error("用户支付宝签约失败,响应信息转码失败");
        }
        return orderString;
    }

    @Override
    public String userQueryAgreementSign(MemberQueryReq memberQueryReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayUserAgreementQueryRequest alipayRequest = new AlipayUserAgreementQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("agreement_no", memberQueryReq.getAgreementNo());
        bizContent.put("personal_product_code", "GENERAL_WITHHOLDING_P");
        String biz = bizContent.toString().replaceAll("\"", "'");
        alipayRequest.setBizContent(biz);
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        try {
            AlipayUserAgreementQueryResponse alipayResponse = alipayClient.execute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                log.info("agreement_no:{}用户查询支付宝签约信息成功", memberQueryReq.getAgreementNo());
                orderString = JSONObject.toJSONString(alipayResponse);
            } else {
                log.info("agreement_no:{}用户查询支付宝签约信息失败", memberQueryReq.getAgreementNo());
            }
        } catch (AlipayApiException e) {
            log.error("用户查询支付宝签约信息错误", e);
        }
        return orderString;
    }

    @Override
    public String userUnAgreementSign(MemberQueryReq memberQueryReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayUserAgreementUnsignRequest alipayRequest = new AlipayUserAgreementUnsignRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("agreement_no", memberQueryReq.getAgreementNo());
        String biz = bizContent.toString().replaceAll("\"", "'");
        alipayRequest.setBizContent(biz);
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        try {
            AlipayUserAgreementUnsignResponse alipayResponse = alipayClient.execute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                log.info("agreement_no:{}用户查询支付宝签约信息成功", memberQueryReq.getAgreementNo());
                orderString = JSONObject.toJSONString(alipayResponse);
            } else {
                log.info("agreement_no:{}用户查询支付宝签约信息失败", memberQueryReq.getAgreementNo());
            }
        } catch (AlipayApiException e) {
            log.error("用户查询支付宝签约信息错误", e);
        }
        return orderString;
    }

    @Override
    public String alipayTradeQuery(TradeReq tradeReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
        if (StringUtils.isNotBlank(tradeReq.getTradeNo()) && StringUtils.isNotBlank(tradeReq.getOutTradeNo())) {
            JSONObject bizContent = new JSONObject();
            bizContent.put("trade_no", tradeReq.getTradeNo());
            bizContent.put("out_trade_no", tradeReq.getOutTradeNo());
            String biz = bizContent.toString().replaceAll("\"", "'");
            alipayRequest.setBizContent(biz);
            // 实例化客户端
            AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
            try {
                AlipayTradeQueryResponse alipayResponse = alipayClient.execute(alipayRequest);
                if (alipayResponse.isSuccess()) {
                    log.info("查询支付宝订单信息成功:trade_no:{},out_trade_no:{}", tradeReq.getTradeNo(), tradeReq.getOutTradeNo());
                    orderString = JSONObject.toJSONString(alipayResponse);
                } else {
                    log.info("查询支付宝订单信息失败:trade_no:{},out_trade_no:{}", tradeReq.getTradeNo(), tradeReq.getOutTradeNo());
                }
            } catch (AlipayApiException e) {
                log.error("查询支付宝订单信息错误", e);
            }
        } else {
            log.info("缺少支付宝订单号和商户订单号必传参数");
        }
        return orderString;
    }

    @Override
    public String alipayTradeRefund(TradeRefundReq tradeRefundReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
        JSONObject bizContent = new JSONObject();
        if (StringUtils.isNotBlank(tradeRefundReq.getTradeNo())) {
            bizContent.put("trade_no", tradeRefundReq.getTradeNo());
        } else if (StringUtils.isNotBlank(tradeRefundReq.getOutTradeNo())) {
            bizContent.put("out_trade_no", tradeRefundReq.getOutTradeNo());
        } else {
            log.info("缺少支付宝订单号或者商户订单号");
            return orderString;
        }
        if (StringUtils.isNotBlank(tradeRefundReq.getRefundAmount())) {
            bizContent.put("refund_amount", tradeRefundReq.getRefundAmount());
        } else {
            log.info("缺少退款金额");
            return orderString;
        }
        String biz = bizContent.toString().replaceAll("\"", "'");
        alipayRequest.setBizContent(biz);
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        try {
            AlipayTradeRefundResponse alipayResponse = alipayClient.execute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                log.info("支付宝订单退款成功:trade_no:{},out_trade_no:{},refund_amount:{}", tradeRefundReq.getTradeNo(), tradeRefundReq.getOutTradeNo(), tradeRefundReq.getRefundAmount());
                orderString = JSONObject.toJSONString(alipayResponse);
            } else {
                log.info("支付宝订单退款失败:trade_no:{},out_trade_no:{},refund_amount:{}", tradeRefundReq.getTradeNo(), tradeRefundReq.getOutTradeNo(), tradeRefundReq.getRefundAmount());
            }
        } catch (AlipayApiException e) {
            log.error("查询支付宝订单信息错误", e);
        }
        return orderString;
    }

    @Override
    public String alipayTradeRefundInfo(TradeRefundReq tradeRefundReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest();
        JSONObject bizContent = new JSONObject();
        if (StringUtils.isNotBlank(tradeRefundReq.getTradeNo())) {
            bizContent.put("trade_no", tradeRefundReq.getTradeNo());
        } else if (StringUtils.isNotBlank(tradeRefundReq.getOutTradeNo())) {
            bizContent.put("out_trade_no", tradeRefundReq.getOutTradeNo());
        } else {
            log.info("缺少支付宝订单号和商户订单号必传参数");
            return orderString;
        }
        String biz = bizContent.toString().replaceAll("\"", "'");
        alipayRequest.setBizContent(biz);
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        try {
            AlipayTradeFastpayRefundQueryResponse alipayResponse = alipayClient.execute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                log.info("查询支付宝订单退款信息成功:trade_no:{},out_trade_no:{}", tradeRefundReq.getTradeNo(), tradeRefundReq.getOutTradeNo());
                orderString = JSONObject.toJSONString(alipayResponse);
            } else {
                log.info("查询支付宝订单退款信息失败:trade_no:{},out_trade_no:{}", tradeRefundReq.getTradeNo(), tradeRefundReq.getOutTradeNo());
            }
        } catch (AlipayApiException e) {
            log.error("查询支付宝订单退款信息错误", e);
        }
        return orderString;
    }

    @Override
    public String alipayTradeClose(TradeCloseReq tradeCloseReq, Properties properties) {
        String orderString = Constant.FAIL;
        AlipayTradeCloseRequest alipayRequest = new AlipayTradeCloseRequest();
        JSONObject bizContent = new JSONObject();
        if (StringUtils.isNotBlank(tradeCloseReq.getTradeNo())) {
            bizContent.put("trade_no", tradeCloseReq.getTradeNo());
        } else if (StringUtils.isNotBlank(tradeCloseReq.getOutTradeNo())) {
            bizContent.put("out_trade_no", tradeCloseReq.getOutTradeNo());
        } else {
            log.info("缺少支付宝订单号或者商户订单号");
            return orderString;
        }
        String biz = bizContent.toString().replaceAll("\"", "'");
        alipayRequest.setBizContent(biz);
        // 实例化客户端
        AlipayClient alipayClient = DefaultAlipayClientFactory.getInstance(properties);
        try {
            AlipayTradeCloseResponse alipayResponse = alipayClient.execute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                log.info("支付宝订单关闭成功:trade_no:{},out_trade_no:{}", tradeCloseReq.getTradeNo(), tradeCloseReq.getOutTradeNo());
                orderString = JSONObject.toJSONString(alipayResponse);
            } else {
                log.info("支付宝订单关闭失败:trade_no:{},out_trade_no:{}", tradeCloseReq.getTradeNo(), tradeCloseReq.getOutTradeNo());
            }
        } catch (AlipayApiException e) {
            log.error("查询支付宝订单信息错误", e);
        }
        return orderString;
    }

    private String getBizContent(CommodityReq commodityReq, Properties properties, String type) {
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", commodityReq.getOutTradeNo());
        //订单金额:元
        bizContent.put("total_amount", commodityReq.getTotalFee());
        //订单标题
        bizContent.put("subject", commodityReq.getSubject());
        bizContent.put("body", commodityReq.getBody());
        switch (type) {
            case "mobilePay":
                log.info("支付宝手机支付");
                bizContent.put("product_code", "QUICK_WAP_PAY");
                //实际收款账号，一般填写商户PID即可
                bizContent.put("seller_id", properties.getProperty("pid"));
                break;
            case "aliPayPc":
                log.info("支付宝PC端支付");
                bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
                //这里有三种模式可供选择,如果在系统内支付，并且是弹出层支付，建议选择模式二、其他模式会跳出当前iframe(亲测有效)
                bizContent.put("qr_pay_mode", "2");
                //实际收款账号，一般填写商户PID即可
                bizContent.put("seller_id", properties.getProperty("pid"));
                break;
            case "withholdPay":
                log.info("支付宝商户代扣");
                // GENERAL_WITHHOLDING 代表商户代扣产品码
                bizContent.put("product_code", "GENERAL_WITHHOLDING");
                bizContent.put("timeout_express", "30m");
                AgreementParams agreementParams = new AgreementParams();
                agreementParams.setAgreementNo(commodityReq.getAgreementNo());
                bizContent.put("agreement_params", JSONObject.toJSONString(agreementParams).replaceAll("\"", "'"));
                break;
            case "aliQrCodePay":
                log.info("支付宝二维码支付");
                // 支付超时，定义为120分钟
                bizContent.put("timeout_express", "120m");
                // 如果该字段为空，则默认为与支付宝签约的商户的PID，也就是appid对应的PID
                bizContent.put("seller_id", properties.getProperty("pid"));
                // (必填) 商户门店编号，通过门店号和商家后台可以配置精准到门店的折扣信息，详询支付宝技术支持
                bizContent.put("store_id", "test_store_id");
                // 业务扩展参数，目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法)，详情请咨询支付宝技术支持
                ExtendParams extendParams = new ExtendParams();
                extendParams.setSysServiceProviderId("2088100200300400500");
                bizContent.put("extend_params", JSONObject.toJSONString(extendParams).replaceAll("\"", "'"));
                break;
            case "appServicePay":
                log.info("app支付服务端");
                bizContent.put("product_code", "QUICK_MSECURITY_PAY");
                bizContent.put("timeout_express", "30m");
                break;
            default:
                log.info("无对应的支付类型");
                break;
        }
        return bizContent.toString().replaceAll("\"", "'");
    }

    private String getNotifyUrl(CommodityReq commodityReq, Properties properties) {
        String notifyUrl = properties.getProperty("notifyUrl");
        if (StringUtils.isNotBlank(commodityReq.getExceptionNotifyUrl())) {
            notifyUrl = commodityReq.getExceptionNotifyUrl();
            log.info("商户调用第三方回调地址：{}", notifyUrl);
        }
        return notifyUrl;
    }

    private String getReturnUrl(CommodityReq commodityReq, Properties properties) {
        String returnUrl = properties.getProperty("returnUrl");
        if (StringUtils.isNotBlank(commodityReq.getExceptionReturnUrl())) {
            returnUrl = commodityReq.getExceptionReturnUrl();
            log.info("调用商品对应的自定义前台回调地址:{}", returnUrl);
        }
        return returnUrl;
    }
}